blob: 4098be6860ea6cf08fd13d36615bd0e37a79f8b9 [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"
Darin Petkov50308cd2011-06-01 18:25:07 -070017
Darin Petkove7cb7f82011-06-03 13:21:51 -070018using std::string;
19using std::vector;
20
Darin Petkov50308cd2011-06-01 18:25:07 -070021namespace shill {
22
Darin Petkove7cb7f82011-06-03 13:21:51 -070023const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
24const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
25const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
26const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
27const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
28const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
29const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
30const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Darin Petkovd1b715b2011-06-02 21:21:22 -070031const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Darin Petkov92c43902011-06-09 20:46:06 -070032const char DHCPConfig::kDHCPCDPathFormatLease[] = "var/run/dhcpcd-%s.lease";
33const char DHCPConfig::kDHCPCDPathFormatPID[] = "var/run/dhcpcd-%s.pid";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070034const char DHCPConfig::kReasonBound[] = "BOUND";
35const char DHCPConfig::kReasonFail[] = "FAIL";
36const char DHCPConfig::kReasonRebind[] = "REBIND";
37const char DHCPConfig::kReasonReboot[] = "REBOOT";
38const char DHCPConfig::kReasonRenew[] = "RENEW";
39
Darin Petkove7cb7f82011-06-03 13:21:51 -070040
Darin Petkovf7897bc2011-06-08 17:13:36 -070041DHCPConfig::DHCPConfig(DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070042 const string &device_name,
Darin Petkov3258a812011-06-23 11:28:45 -070043 GLib *glib)
Darin Petkovf65e9282011-06-21 14:29:56 -070044 : IPConfig(device_name),
Darin Petkovd1b715b2011-06-02 21:21:22 -070045 provider_(provider),
Darin Petkovf7897bc2011-06-08 17:13:36 -070046 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070047 child_watch_tag_(0),
48 root_("/"),
Darin Petkovf7897bc2011-06-08 17:13:36 -070049 glib_(glib) {
Chris Masone43b48a12011-07-01 13:37:07 -070050 RegisterConstString(flimflam::kAddressProperty, &(properties().address));
Darin Petkovf65e9282011-06-21 14:29:56 -070051 VLOG(2) << __func__ << ": " << device_name;
Darin Petkov50308cd2011-06-01 18:25:07 -070052}
53
54DHCPConfig::~DHCPConfig() {
Darin Petkovf65e9282011-06-21 14:29:56 -070055 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -070056
57 // Don't leave behind dhcpcd running.
58 Stop();
59
Darin Petkov98dd6a02011-06-10 15:12:57 -070060 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070061 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070062}
63
Darin Petkov92c43902011-06-09 20:46:06 -070064bool DHCPConfig::RequestIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070065 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -070066 if (!pid_) {
67 return Start();
68 }
69 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070070 LOG(ERROR) << "Unable to request IP before acquiring destination.";
71 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070072 }
Darin Petkov92c43902011-06-09 20:46:06 -070073 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070074}
75
Darin Petkov92c43902011-06-09 20:46:06 -070076bool DHCPConfig::RenewIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070077 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070078 if (!pid_) {
79 return false;
80 }
81 if (!proxy_.get()) {
82 LOG(ERROR) << "Unable to renew IP before acquiring destination.";
Darin Petkovd1b715b2011-06-02 21:21:22 -070083 return false;
84 }
Darin Petkovf65e9282011-06-21 14:29:56 -070085 proxy_->DoRebind(device_name());
Darin Petkovd1b715b2011-06-02 21:21:22 -070086 return true;
87}
88
Darin Petkov92c43902011-06-09 20:46:06 -070089bool DHCPConfig::ReleaseIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070090 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070091 if (!pid_) {
92 return true;
93 }
94 if (!proxy_.get()) {
95 LOG(ERROR) << "Unable to release IP before acquiring destination.";
96 return false;
97 }
Darin Petkovf65e9282011-06-21 14:29:56 -070098 proxy_->DoRelease(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -070099 Stop();
100 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700101}
102
Darin Petkovd1b715b2011-06-02 21:21:22 -0700103void DHCPConfig::InitProxy(DBus::Connection *connection, const char *service) {
104 if (!proxy_.get()) {
105 proxy_.reset(new DHCPCDProxy(connection, service));
106 }
107}
108
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700109void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700110 const Configuration &configuration) {
111 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700112 if (reason == kReasonFail) {
113 LOG(ERROR) << "Received failure event from DHCP client.";
114 UpdateProperties(IPConfig::Properties(), false);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700115 return;
116 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700117 if (reason != kReasonBound &&
118 reason != kReasonRebind &&
119 reason != kReasonReboot &&
120 reason != kReasonRenew) {
121 LOG(WARNING) << "Event ignored.";
122 return;
123 }
124 IPConfig::Properties properties;
125 CHECK(ParseConfiguration(configuration, &properties));
126 UpdateProperties(properties, true);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700127}
128
Darin Petkovd1b715b2011-06-02 21:21:22 -0700129bool DHCPConfig::Start() {
Darin Petkovf65e9282011-06-21 14:29:56 -0700130 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700131
Darin Petkov98dd6a02011-06-10 15:12:57 -0700132 char *argv[4] = {
133 const_cast<char *>(kDHCPCDPath),
134 const_cast<char *>("-B"), // foreground
Darin Petkovf65e9282011-06-21 14:29:56 -0700135 const_cast<char *>(device_name().c_str()),
Darin Petkov98dd6a02011-06-10 15:12:57 -0700136 NULL
137 };
138 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700139
Darin Petkov98dd6a02011-06-10 15:12:57 -0700140 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700141 if (!glib_->SpawnAsync(NULL,
142 argv,
143 envp,
144 G_SPAWN_DO_NOT_REAP_CHILD,
145 NULL,
146 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700147 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700148 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700149 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
150 return false;
151 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700152 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700153 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700154 CHECK(!child_watch_tag_);
155 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700156 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700157}
158
Darin Petkov92c43902011-06-09 20:46:06 -0700159void DHCPConfig::Stop() {
160 if (pid_) {
161 VLOG(2) << "Terminating " << pid_;
162 PLOG_IF(ERROR, kill(pid_, SIGTERM) < 0);
163 }
164}
165
Darin Petkov98dd6a02011-06-10 15:12:57 -0700166bool DHCPConfig::Restart() {
167 // Check to ensure that this instance doesn't get destroyed in the middle of
168 // this call. If stopping a running client while there's only one reference to
169 // this instance, we will end up destroying it when the PID is unbound from
170 // the Provider. Since the Provider doesn't invoke Restart, this would mean
171 // that Restart was erroneously executed through a bare reference.
172 CHECK(!pid_ || !HasOneRef());
173 Stop();
174 if (pid_) {
175 provider_->UnbindPID(pid_);
176 }
177 CleanupClientState();
178 return Start();
179}
180
Darin Petkove7cb7f82011-06-03 13:21:51 -0700181string DHCPConfig::GetIPv4AddressString(unsigned int address) {
182 char str[INET_ADDRSTRLEN];
183 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
184 return str;
185 }
186 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
187 return "";
188}
189
190bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
191 IPConfig::Properties *properties) {
192 VLOG(2) << __func__;
Chris Masone43b48a12011-07-01 13:37:07 -0700193 properties->method = flimflam::kTypeDHCP;
Paul Stewartc39f1132011-06-22 12:02:28 -0700194 properties->address_family = IPConfig::kAddressFamilyIPv4;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700195 for (Configuration::const_iterator it = configuration.begin();
196 it != configuration.end(); ++it) {
197 const string &key = it->first;
198 const DBus::Variant &value = it->second;
199 VLOG(2) << "Processing key: " << key;
200 if (key == kConfigurationKeyIPAddress) {
201 properties->address = GetIPv4AddressString(value.reader().get_uint32());
202 if (properties->address.empty()) {
203 return false;
204 }
205 } else if (key == kConfigurationKeySubnetCIDR) {
206 properties->subnet_cidr = value.reader().get_byte();
207 } else if (key == kConfigurationKeyBroadcastAddress) {
208 properties->broadcast_address =
209 GetIPv4AddressString(value.reader().get_uint32());
210 if (properties->broadcast_address.empty()) {
211 return false;
212 }
213 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700214 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700215 if (routers.empty()) {
216 LOG(ERROR) << "No routers provided.";
217 return false;
218 }
219 properties->gateway = GetIPv4AddressString(routers[0]);
220 if (properties->gateway.empty()) {
221 return false;
222 }
223 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700224 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700225 for (vector<unsigned int>::const_iterator it = servers.begin();
226 it != servers.end(); ++it) {
227 string server = GetIPv4AddressString(*it);
228 if (server.empty()) {
229 return false;
230 }
231 properties->dns_servers.push_back(server);
232 }
233 } else if (key == kConfigurationKeyDomainName) {
234 properties->domain_name = value.reader().get_string();
235 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700236 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700237 } else if (key == kConfigurationKeyMTU) {
238 int mtu = value.reader().get_uint16();
239 if (mtu >= 576) {
240 properties->mtu = mtu;
241 }
242 } else {
243 VLOG(2) << "Key ignored.";
244 }
245 }
246 return true;
247}
248
Darin Petkov92c43902011-06-09 20:46:06 -0700249void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
250 VLOG(2) << "pid " << pid << " exit status " << status;
251 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
252 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700253 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700254 config->CleanupClientState();
255
256 // |config| instance may be destroyed after this call.
257 config->provider_->UnbindPID(pid);
258}
259
260void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700261 if (child_watch_tag_) {
262 glib_->SourceRemove(child_watch_tag_);
263 child_watch_tag_ = 0;
264 }
265 if (pid_) {
266 glib_->SpawnClosePID(pid_);
267 pid_ = 0;
268 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700269 proxy_.reset();
Darin Petkov92c43902011-06-09 20:46:06 -0700270 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatLease,
Darin Petkovf65e9282011-06-21 14:29:56 -0700271 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700272 false);
273 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatPID,
Darin Petkovf65e9282011-06-21 14:29:56 -0700274 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700275 false);
276}
277
Darin Petkov50308cd2011-06-01 18:25:07 -0700278} // namespace shill