blob: e01c772cb9a053d9a270907728da987f5331f81e [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>
Darin Petkovd1b715b2011-06-02 21:21:22 -070012
13#include "shill/dhcpcd_proxy.h"
14#include "shill/dhcp_provider.h"
Darin Petkov3258a812011-06-23 11:28:45 -070015#include "shill/glib.h"
Darin Petkov50308cd2011-06-01 18:25:07 -070016
Darin Petkove7cb7f82011-06-03 13:21:51 -070017using std::string;
18using std::vector;
19
Darin Petkov50308cd2011-06-01 18:25:07 -070020namespace shill {
21
Darin Petkove7cb7f82011-06-03 13:21:51 -070022const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
23const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
24const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
25const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
26const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
27const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
28const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
29const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Darin Petkovd1b715b2011-06-02 21:21:22 -070030const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Darin Petkov92c43902011-06-09 20:46:06 -070031const char DHCPConfig::kDHCPCDPathFormatLease[] = "var/run/dhcpcd-%s.lease";
32const char DHCPConfig::kDHCPCDPathFormatPID[] = "var/run/dhcpcd-%s.pid";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070033const char DHCPConfig::kReasonBound[] = "BOUND";
34const char DHCPConfig::kReasonFail[] = "FAIL";
35const char DHCPConfig::kReasonRebind[] = "REBIND";
36const char DHCPConfig::kReasonReboot[] = "REBOOT";
37const char DHCPConfig::kReasonRenew[] = "RENEW";
38
Darin Petkove7cb7f82011-06-03 13:21:51 -070039
Darin Petkovf7897bc2011-06-08 17:13:36 -070040DHCPConfig::DHCPConfig(DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070041 const string &device_name,
Darin Petkov3258a812011-06-23 11:28:45 -070042 GLib *glib)
Darin Petkovf65e9282011-06-21 14:29:56 -070043 : IPConfig(device_name),
Darin Petkovd1b715b2011-06-02 21:21:22 -070044 provider_(provider),
Darin Petkovf7897bc2011-06-08 17:13:36 -070045 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070046 child_watch_tag_(0),
47 root_("/"),
Darin Petkovf7897bc2011-06-08 17:13:36 -070048 glib_(glib) {
Darin Petkovf65e9282011-06-21 14:29:56 -070049 VLOG(2) << __func__ << ": " << device_name;
Darin Petkov50308cd2011-06-01 18:25:07 -070050}
51
52DHCPConfig::~DHCPConfig() {
Darin Petkovf65e9282011-06-21 14:29:56 -070053 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -070054
55 // Don't leave behind dhcpcd running.
56 Stop();
57
Darin Petkov98dd6a02011-06-10 15:12:57 -070058 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070059 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070060}
61
Darin Petkov92c43902011-06-09 20:46:06 -070062bool DHCPConfig::RequestIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070063 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -070064 if (!pid_) {
65 return Start();
66 }
67 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070068 LOG(ERROR) << "Unable to request IP before acquiring destination.";
69 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070070 }
Darin Petkov92c43902011-06-09 20:46:06 -070071 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070072}
73
Darin Petkov92c43902011-06-09 20:46:06 -070074bool DHCPConfig::RenewIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070075 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070076 if (!pid_) {
77 return false;
78 }
79 if (!proxy_.get()) {
80 LOG(ERROR) << "Unable to renew IP before acquiring destination.";
Darin Petkovd1b715b2011-06-02 21:21:22 -070081 return false;
82 }
Darin Petkovf65e9282011-06-21 14:29:56 -070083 proxy_->DoRebind(device_name());
Darin Petkovd1b715b2011-06-02 21:21:22 -070084 return true;
85}
86
Darin Petkov92c43902011-06-09 20:46:06 -070087bool DHCPConfig::ReleaseIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070088 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070089 if (!pid_) {
90 return true;
91 }
92 if (!proxy_.get()) {
93 LOG(ERROR) << "Unable to release IP before acquiring destination.";
94 return false;
95 }
Darin Petkovf65e9282011-06-21 14:29:56 -070096 proxy_->DoRelease(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -070097 Stop();
98 return true;
Darin Petkov92c43902011-06-09 20:46:06 -070099}
100
Darin Petkovd1b715b2011-06-02 21:21:22 -0700101void DHCPConfig::InitProxy(DBus::Connection *connection, const char *service) {
102 if (!proxy_.get()) {
103 proxy_.reset(new DHCPCDProxy(connection, service));
104 }
105}
106
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700107void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700108 const Configuration &configuration) {
109 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700110 if (reason == kReasonFail) {
111 LOG(ERROR) << "Received failure event from DHCP client.";
112 UpdateProperties(IPConfig::Properties(), false);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700113 return;
114 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700115 if (reason != kReasonBound &&
116 reason != kReasonRebind &&
117 reason != kReasonReboot &&
118 reason != kReasonRenew) {
119 LOG(WARNING) << "Event ignored.";
120 return;
121 }
122 IPConfig::Properties properties;
123 CHECK(ParseConfiguration(configuration, &properties));
124 UpdateProperties(properties, true);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700125}
126
Darin Petkovd1b715b2011-06-02 21:21:22 -0700127bool DHCPConfig::Start() {
Darin Petkovf65e9282011-06-21 14:29:56 -0700128 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700129
Darin Petkov98dd6a02011-06-10 15:12:57 -0700130 char *argv[4] = {
131 const_cast<char *>(kDHCPCDPath),
132 const_cast<char *>("-B"), // foreground
Darin Petkovf65e9282011-06-21 14:29:56 -0700133 const_cast<char *>(device_name().c_str()),
Darin Petkov98dd6a02011-06-10 15:12:57 -0700134 NULL
135 };
136 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700137
Darin Petkov98dd6a02011-06-10 15:12:57 -0700138 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700139 if (!glib_->SpawnAsync(NULL,
140 argv,
141 envp,
142 G_SPAWN_DO_NOT_REAP_CHILD,
143 NULL,
144 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700145 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700146 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700147 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
148 return false;
149 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700150 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700151 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700152 CHECK(!child_watch_tag_);
153 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700154 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700155}
156
Darin Petkov92c43902011-06-09 20:46:06 -0700157void DHCPConfig::Stop() {
158 if (pid_) {
159 VLOG(2) << "Terminating " << pid_;
160 PLOG_IF(ERROR, kill(pid_, SIGTERM) < 0);
161 }
162}
163
Darin Petkov98dd6a02011-06-10 15:12:57 -0700164bool DHCPConfig::Restart() {
165 // Check to ensure that this instance doesn't get destroyed in the middle of
166 // this call. If stopping a running client while there's only one reference to
167 // this instance, we will end up destroying it when the PID is unbound from
168 // the Provider. Since the Provider doesn't invoke Restart, this would mean
169 // that Restart was erroneously executed through a bare reference.
170 CHECK(!pid_ || !HasOneRef());
171 Stop();
172 if (pid_) {
173 provider_->UnbindPID(pid_);
174 }
175 CleanupClientState();
176 return Start();
177}
178
Darin Petkove7cb7f82011-06-03 13:21:51 -0700179string DHCPConfig::GetIPv4AddressString(unsigned int address) {
180 char str[INET_ADDRSTRLEN];
181 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
182 return str;
183 }
184 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
185 return "";
186}
187
188bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
189 IPConfig::Properties *properties) {
190 VLOG(2) << __func__;
191 for (Configuration::const_iterator it = configuration.begin();
192 it != configuration.end(); ++it) {
193 const string &key = it->first;
194 const DBus::Variant &value = it->second;
195 VLOG(2) << "Processing key: " << key;
196 if (key == kConfigurationKeyIPAddress) {
197 properties->address = GetIPv4AddressString(value.reader().get_uint32());
198 if (properties->address.empty()) {
199 return false;
200 }
201 } else if (key == kConfigurationKeySubnetCIDR) {
202 properties->subnet_cidr = value.reader().get_byte();
203 } else if (key == kConfigurationKeyBroadcastAddress) {
204 properties->broadcast_address =
205 GetIPv4AddressString(value.reader().get_uint32());
206 if (properties->broadcast_address.empty()) {
207 return false;
208 }
209 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700210 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700211 if (routers.empty()) {
212 LOG(ERROR) << "No routers provided.";
213 return false;
214 }
215 properties->gateway = GetIPv4AddressString(routers[0]);
216 if (properties->gateway.empty()) {
217 return false;
218 }
219 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700220 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700221 for (vector<unsigned int>::const_iterator it = servers.begin();
222 it != servers.end(); ++it) {
223 string server = GetIPv4AddressString(*it);
224 if (server.empty()) {
225 return false;
226 }
227 properties->dns_servers.push_back(server);
228 }
229 } else if (key == kConfigurationKeyDomainName) {
230 properties->domain_name = value.reader().get_string();
231 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700232 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700233 } else if (key == kConfigurationKeyMTU) {
234 int mtu = value.reader().get_uint16();
235 if (mtu >= 576) {
236 properties->mtu = mtu;
237 }
238 } else {
239 VLOG(2) << "Key ignored.";
240 }
241 }
242 return true;
243}
244
Darin Petkov92c43902011-06-09 20:46:06 -0700245void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
246 VLOG(2) << "pid " << pid << " exit status " << status;
247 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
248 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700249 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700250 config->CleanupClientState();
251
252 // |config| instance may be destroyed after this call.
253 config->provider_->UnbindPID(pid);
254}
255
256void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700257 if (child_watch_tag_) {
258 glib_->SourceRemove(child_watch_tag_);
259 child_watch_tag_ = 0;
260 }
261 if (pid_) {
262 glib_->SpawnClosePID(pid_);
263 pid_ = 0;
264 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700265 proxy_.reset();
Darin Petkov92c43902011-06-09 20:46:06 -0700266 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatLease,
Darin Petkovf65e9282011-06-21 14:29:56 -0700267 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700268 false);
269 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatPID,
Darin Petkovf65e9282011-06-21 14:29:56 -0700270 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700271 false);
272}
273
Darin Petkov50308cd2011-06-01 18:25:07 -0700274} // namespace shill