blob: 5a4a30740ce9547142d2a52e7d42b52cd18f4ec6 [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 Petkov50308cd2011-06-01 18:25:07 -070018
Darin Petkove7cb7f82011-06-03 13:21:51 -070019using std::string;
20using std::vector;
21
Darin Petkov50308cd2011-06-01 18:25:07 -070022namespace shill {
23
Darin Petkove7cb7f82011-06-03 13:21:51 -070024const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
25const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
26const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
27const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
28const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
29const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
30const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
31const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Darin Petkovd1b715b2011-06-02 21:21:22 -070032const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Darin Petkov92c43902011-06-09 20:46:06 -070033const char DHCPConfig::kDHCPCDPathFormatLease[] = "var/run/dhcpcd-%s.lease";
34const char DHCPConfig::kDHCPCDPathFormatPID[] = "var/run/dhcpcd-%s.pid";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070035const char DHCPConfig::kReasonBound[] = "BOUND";
36const char DHCPConfig::kReasonFail[] = "FAIL";
37const char DHCPConfig::kReasonRebind[] = "REBIND";
38const char DHCPConfig::kReasonReboot[] = "REBOOT";
39const char DHCPConfig::kReasonRenew[] = "RENEW";
40
Darin Petkove7cb7f82011-06-03 13:21:51 -070041
Darin Petkovf7897bc2011-06-08 17:13:36 -070042DHCPConfig::DHCPConfig(DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070043 const string &device_name,
Darin Petkov3258a812011-06-23 11:28:45 -070044 GLib *glib)
Darin Petkovf65e9282011-06-21 14:29:56 -070045 : IPConfig(device_name),
Darin Petkovd1b715b2011-06-02 21:21:22 -070046 provider_(provider),
Darin Petkovf7897bc2011-06-08 17:13:36 -070047 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070048 child_watch_tag_(0),
49 root_("/"),
Darin Petkovf7897bc2011-06-08 17:13:36 -070050 glib_(glib) {
Chris Masone27c4aa52011-07-02 13:10:14 -070051 store_.RegisterConstString(flimflam::kAddressProperty,
52 &(properties().address));
Darin Petkovf65e9282011-06-21 14:29:56 -070053 VLOG(2) << __func__ << ": " << device_name;
Darin Petkov50308cd2011-06-01 18:25:07 -070054}
55
56DHCPConfig::~DHCPConfig() {
Darin Petkovf65e9282011-06-21 14:29:56 -070057 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -070058
59 // Don't leave behind dhcpcd running.
60 Stop();
61
Darin Petkov98dd6a02011-06-10 15:12:57 -070062 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070063 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070064}
65
Darin Petkov92c43902011-06-09 20:46:06 -070066bool DHCPConfig::RequestIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070067 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -070068 if (!pid_) {
69 return Start();
70 }
71 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070072 LOG(ERROR) << "Unable to request IP before acquiring destination.";
73 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070074 }
Darin Petkov92c43902011-06-09 20:46:06 -070075 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070076}
77
Darin Petkov92c43902011-06-09 20:46:06 -070078bool DHCPConfig::RenewIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070079 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070080 if (!pid_) {
81 return false;
82 }
83 if (!proxy_.get()) {
84 LOG(ERROR) << "Unable to renew IP before acquiring destination.";
Darin Petkovd1b715b2011-06-02 21:21:22 -070085 return false;
86 }
Darin Petkovf65e9282011-06-21 14:29:56 -070087 proxy_->DoRebind(device_name());
Darin Petkovd1b715b2011-06-02 21:21:22 -070088 return true;
89}
90
Darin Petkov92c43902011-06-09 20:46:06 -070091bool DHCPConfig::ReleaseIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070092 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070093 if (!pid_) {
94 return true;
95 }
96 if (!proxy_.get()) {
97 LOG(ERROR) << "Unable to release IP before acquiring destination.";
98 return false;
99 }
Darin Petkovf65e9282011-06-21 14:29:56 -0700100 proxy_->DoRelease(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -0700101 Stop();
102 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700103}
104
Darin Petkovd1b715b2011-06-02 21:21:22 -0700105void DHCPConfig::InitProxy(DBus::Connection *connection, const char *service) {
106 if (!proxy_.get()) {
107 proxy_.reset(new DHCPCDProxy(connection, service));
108 }
109}
110
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700111void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700112 const Configuration &configuration) {
113 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700114 if (reason == kReasonFail) {
115 LOG(ERROR) << "Received failure event from DHCP client.";
116 UpdateProperties(IPConfig::Properties(), false);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700117 return;
118 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700119 if (reason != kReasonBound &&
120 reason != kReasonRebind &&
121 reason != kReasonReboot &&
122 reason != kReasonRenew) {
123 LOG(WARNING) << "Event ignored.";
124 return;
125 }
126 IPConfig::Properties properties;
127 CHECK(ParseConfiguration(configuration, &properties));
128 UpdateProperties(properties, true);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700129}
130
Darin Petkovd1b715b2011-06-02 21:21:22 -0700131bool DHCPConfig::Start() {
Darin Petkovf65e9282011-06-21 14:29:56 -0700132 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700133
Darin Petkov98dd6a02011-06-10 15:12:57 -0700134 char *argv[4] = {
135 const_cast<char *>(kDHCPCDPath),
136 const_cast<char *>("-B"), // foreground
Darin Petkovf65e9282011-06-21 14:29:56 -0700137 const_cast<char *>(device_name().c_str()),
Darin Petkov98dd6a02011-06-10 15:12:57 -0700138 NULL
139 };
140 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700141
Darin Petkov98dd6a02011-06-10 15:12:57 -0700142 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700143 if (!glib_->SpawnAsync(NULL,
144 argv,
145 envp,
146 G_SPAWN_DO_NOT_REAP_CHILD,
147 NULL,
148 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700149 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700150 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700151 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
152 return false;
153 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700154 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700155 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700156 CHECK(!child_watch_tag_);
157 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700158 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700159}
160
Darin Petkov92c43902011-06-09 20:46:06 -0700161void DHCPConfig::Stop() {
162 if (pid_) {
163 VLOG(2) << "Terminating " << pid_;
164 PLOG_IF(ERROR, kill(pid_, SIGTERM) < 0);
165 }
166}
167
Darin Petkov98dd6a02011-06-10 15:12:57 -0700168bool DHCPConfig::Restart() {
169 // Check to ensure that this instance doesn't get destroyed in the middle of
170 // this call. If stopping a running client while there's only one reference to
171 // this instance, we will end up destroying it when the PID is unbound from
172 // the Provider. Since the Provider doesn't invoke Restart, this would mean
173 // that Restart was erroneously executed through a bare reference.
174 CHECK(!pid_ || !HasOneRef());
175 Stop();
176 if (pid_) {
177 provider_->UnbindPID(pid_);
178 }
179 CleanupClientState();
180 return Start();
181}
182
Darin Petkove7cb7f82011-06-03 13:21:51 -0700183string DHCPConfig::GetIPv4AddressString(unsigned int address) {
184 char str[INET_ADDRSTRLEN];
185 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
186 return str;
187 }
188 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
189 return "";
190}
191
192bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
193 IPConfig::Properties *properties) {
194 VLOG(2) << __func__;
Chris Masone43b48a12011-07-01 13:37:07 -0700195 properties->method = flimflam::kTypeDHCP;
Paul Stewart1d18e8c2011-07-15 11:00:31 -0700196 properties->address_family = IPAddress::kAddressFamilyIPv4;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700197 for (Configuration::const_iterator it = configuration.begin();
198 it != configuration.end(); ++it) {
199 const string &key = it->first;
200 const DBus::Variant &value = it->second;
201 VLOG(2) << "Processing key: " << key;
202 if (key == kConfigurationKeyIPAddress) {
203 properties->address = GetIPv4AddressString(value.reader().get_uint32());
204 if (properties->address.empty()) {
205 return false;
206 }
207 } else if (key == kConfigurationKeySubnetCIDR) {
208 properties->subnet_cidr = value.reader().get_byte();
209 } else if (key == kConfigurationKeyBroadcastAddress) {
210 properties->broadcast_address =
211 GetIPv4AddressString(value.reader().get_uint32());
212 if (properties->broadcast_address.empty()) {
213 return false;
214 }
215 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700216 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700217 if (routers.empty()) {
218 LOG(ERROR) << "No routers provided.";
219 return false;
220 }
221 properties->gateway = GetIPv4AddressString(routers[0]);
222 if (properties->gateway.empty()) {
223 return false;
224 }
225 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700226 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700227 for (vector<unsigned int>::const_iterator it = servers.begin();
228 it != servers.end(); ++it) {
229 string server = GetIPv4AddressString(*it);
230 if (server.empty()) {
231 return false;
232 }
233 properties->dns_servers.push_back(server);
234 }
235 } else if (key == kConfigurationKeyDomainName) {
236 properties->domain_name = value.reader().get_string();
237 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700238 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700239 } else if (key == kConfigurationKeyMTU) {
240 int mtu = value.reader().get_uint16();
241 if (mtu >= 576) {
242 properties->mtu = mtu;
243 }
244 } else {
245 VLOG(2) << "Key ignored.";
246 }
247 }
248 return true;
249}
250
Darin Petkov92c43902011-06-09 20:46:06 -0700251void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
252 VLOG(2) << "pid " << pid << " exit status " << status;
253 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
254 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700255 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700256 config->CleanupClientState();
257
258 // |config| instance may be destroyed after this call.
259 config->provider_->UnbindPID(pid);
260}
261
262void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700263 if (child_watch_tag_) {
264 glib_->SourceRemove(child_watch_tag_);
265 child_watch_tag_ = 0;
266 }
267 if (pid_) {
268 glib_->SpawnClosePID(pid_);
269 pid_ = 0;
270 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700271 proxy_.reset();
Darin Petkov92c43902011-06-09 20:46:06 -0700272 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatLease,
Darin Petkovf65e9282011-06-21 14:29:56 -0700273 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700274 false);
275 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatPID,
Darin Petkovf65e9282011-06-21 14:29:56 -0700276 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700277 false);
278}
279
Darin Petkov50308cd2011-06-01 18:25:07 -0700280} // namespace shill